home *** CD-ROM | disk | FTP | other *** search
/ Programming Sound Cards / Programming Sound Cards.iso / sound_06 / demo2.c < prev    next >
C/C++ Source or Header  |  1995-01-01  |  27KB  |  1,038 lines

  1. /*
  2.     DEMO2.C, Low level demonstration program.
  3.  
  4.     Copyright Ad Lib Inc, 1988.
  5.  
  6.     17-Oct-88, Dale Glowinski, Marc Savary, Ad Lib Inc.
  7.  
  8.     This is a demonstration program which shows an alternate method for
  9.     generating sound.  Its general strategy is to start a sound, and then
  10.     adjust it for volume or frequency at regular intervals.  This eliminates
  11.     the need to use a music (.ROL) file which would be played as a piece
  12.     of music.  (However, before coding a sound effect, it is a good idea
  13.     to develop an approximation of the desired sound using the
  14.     Visual Composer.)  This method is only useful for sounds which are
  15.     repetitive or continuous.
  16.  
  17.     The program uses several routines in ADLIB.C, which is slightly
  18.     different from the original version, but only in that several "static"
  19.     statements have been removed to make several lower level routines
  20.     available to this module.
  21.  
  22.     Compiled with Microsoft C, V5.0, as follows:
  23.        cl -AS -Zi -Ox -Gs demo2.c adlib.obj
  24.  
  25.     Some of the programming was done in a rather arbitrary fashion in order
  26.     to save time and to keep the code as simple as possible:
  27.     - Volumes are set to a value which was based on whatever sounded best.
  28.       Depending on what you wish to do, variables and passing values might
  29.       be in order.  Ditto for pitches.
  30.     - A simple counter was used to create the timing, so that the speed
  31.       of the sounds produced by the executable file will vary with the
  32.       speed of the processor.  Certainly, one would have to use the real
  33.       timer in a real application.  (The timing counter can be changed by
  34.       using the "/t" execution option.)
  35.  
  36. */
  37.  
  38.  
  39. #include  <stdlib.h>
  40. #include  <fcntl.h>
  41. #include  <sys\types.h>
  42. #include  <sys\stat.h>
  43. #include  <io.h>
  44. #include  <string.h>
  45.  
  46. #include  "adlib.h"
  47.  
  48. /* synthesizer modes */
  49. #define  MELODIC     0
  50. #define  PERCUSSIVE  1
  51. #define  MODE        MELODIC
  52.  
  53. /* computer keyboard scan codes */
  54. #define  LEFT        75
  55. #define  RIGHT       77
  56. #define  UP          72
  57. #define  DOWN        80
  58. #define  PAGEUP      73
  59. #define  PAGEDOWN    81
  60.  
  61. /* flags used for Set_Instrument() */
  62. #define  RELEASE     1
  63. #define  NO_RELEASE  0
  64.  
  65. #if (MODE == PERCUSSIVE)
  66. #define  NR_VOICES   11
  67. #else
  68. #define  NR_VOICES   9
  69. #endif
  70.  
  71. #define  SIREN       0
  72. #define  COPTER      1
  73. #define  PLANEa      2
  74. #define  PLANEb      3
  75. #define  BOMBa       4
  76. #define  BOMBb       5
  77. #define  BOMBc       6
  78. #define  BOMBd       7
  79. #define  JETa        8
  80. #define  JETb        9
  81. #define  JETc        10
  82. #define  JETd        11
  83. #define  JETe        12
  84. #define  FAKE_ID     13
  85. #define  NR_SOUNDS   14
  86.  
  87. /* These are declared in ADLIB.C */
  88. extern char slotVoice [][2];
  89. extern char slotPerc  [][2];
  90. extern NoteOff (int);
  91. extern NoteOn  (int);
  92.  
  93. /* Timbre information for each sound */
  94. unsigned char op [NR_SOUNDS][2][30];
  95.  
  96. /* These arrays contain information for each sound */
  97. unsigned int fnum [NR_SOUNDS];
  98. int octave [NR_SOUNDS];
  99. int volume [NR_SOUNDS];
  100. int myVoice [NR_SOUNDS];        /* voice number being used */
  101.  
  102. int voices [NR_VOICES];         /* used to allocate voices */
  103.  
  104. int mode;                       /* card mode */
  105. int start_time = 100;
  106. int timing = 100;
  107.  
  108. /* These routines are redefined to clarify their appearance in the code. */
  109. #define  SetVoiceVolume(x,y)    SetVoiceVolume(myVoice[x],y)
  110. #define  NoteOn(x)              NoteOn(myVoice[x]);
  111. #define  NoteOff(x)             NoteOff(myVoice[x]);
  112.  
  113.  
  114. main( argc, argv)
  115.    int     argc;
  116.    char    *argv[];
  117. {
  118.    int i;
  119.    extern SoundColdInit();
  120.  
  121.    for ( i = 1, argv++; i < argc; i++, argv++)
  122.    {
  123.      strupr ( *argv);
  124.      if ( ! strncmp( "/T", *argv, 2)) start_time = atoi (*argv + 2);
  125.    }
  126.  
  127.    Instrument_Init ();
  128.    Demo_Init ();
  129.  
  130.    Choose_Sound ();   /* user interface routine */
  131.  
  132.    Stifle ();
  133.    SoundColdInit (0x388);
  134. }
  135.  
  136.  
  137. /*-----------------------------------------------------------------------*/
  138. float frq_low [12] = {16.352, 17.324, 18.354, 19.445, 20.601, 21.826,
  139.                       23.124, 24.499, 25.956, 27.500, 29.135, 30.867};
  140.  
  141. unsigned int freq_nums [12];
  142.  
  143. /*
  144. frq_low is a table of frequencies for the first 12 pitches.  If Note_A is
  145. one octave higher than Note_B, then the frequency of Note_B is twice that
  146. of Note_A.  We can then use this relationship to find the frequency for any
  147. note by multiplying the value in frq_low (pitch % 12) by a power of 2, where
  148. the power of two is the octave (b = pitch / 12 - 1).  This can be expressed
  149. as follows:
  150.     frequency = frq_low [pitch % 12] * (2 ** b)
  151.  
  152. freq_nums is a table of f-numbers given a pitch (modulo 12).  The card uses
  153. the f-number and the octave bits to determine the pitch it will output.
  154. Note that the f-numbers preserve the same power of 2 relationship between
  155. octaves as for frequency.
  156.  
  157. The f-numbers can be calculated using a transformed version of the formula
  158. on page 47 of the programmer's manual:
  159.    1.  fnum = freq * (2 ** 20-b) / 50 kHz
  160.    2.  fnum = freq * (2 ** 20) * (2 ** -b) / 50000      (from 1)
  161.    3.  fnum = freq * 20.971 * (2 ** -b)                 (from 2)
  162.    4.  freq = frq_low [pitch % 12] * (2 ** b)
  163.    5.  fnum = frq_low [pitch % 12] * 20.971             (from 3 & 4)
  164. (b is the octave less 1 so that 0 <= b <= 7.)
  165. Thus, the f-numbers are the integer results of multiplying the values in
  166. frq_low by 20.971.
  167.  
  168. This gives a range of 343 to 685 for the f-numbers.  However, the f-number
  169. register is 9 bits and can hold an unsigned value of 0 to 1023.  The range
  170. outside of 343-685 can be used, but it should be noted that in the lower
  171. ranges, a small change in f-number will yield a larger change in output
  172. frequency.  By always using the range 343-685, one will have the same
  173. incremental degree of control between pitches regardless of the octave.
  174. */
  175.  
  176.  
  177. /*-----------------------------------------------------------------------
  178. This sets up the table of f-numbers for a given pitch.  This actually could
  179. be calculated beforehand and the array initialized to the calculated values,
  180. but it is done here for illustrative purposes.  */
  181.  
  182. SetUp_FNums ()
  183. {
  184.    int n;
  185.    float f;
  186.    for (n=0; n < 12; n++) {
  187.       f = frq_low [n] * 20.971;
  188.       freq_nums [n] = (unsigned int) f;
  189.  
  190.       /* Round number up if necessary */
  191.       f -= (float) freq_nums [n];
  192.       if (f > 0.5) freq_nums [n]++;
  193.    }
  194. }
  195.  
  196.  
  197. /*-----------------------------------------------------------------------
  198. This does a note-on for the passed f-num.  If the note is already on,
  199. only the frequency of the note will change.  */
  200.  
  201. Out_Freq (sound_ID, fNbr, octave)
  202.     unsigned int sound_ID;
  203.     unsigned int fNbr;              /* f-number for card */
  204.     unsigned int octave;
  205. {
  206.     unsigned int t1;
  207.  
  208.     octave -= 1;
  209.     while (fNbr > 1023) {
  210.        fNbr >>= 1;
  211.        octave++;
  212.     }
  213.     while (fNbr < freq_nums [0]) {
  214.        fNbr <<= 1;
  215.        octave--;
  216.     }
  217.  
  218.     SndOutput (0xA0 + myVoice [sound_ID], fNbr);
  219.  
  220.     t1 = 0x20 | (octave << 2) | (0x3 & (fNbr >> 8));
  221.     SndOutput (0xB0 + myVoice [sound_ID], t1);
  222.     return (octave);
  223. }
  224.  
  225.  
  226. /*-----------------------------------------------------------------------
  227. Initialize the card and set up several variables. */
  228.  
  229. Demo_Init ()
  230. {
  231.    extern SoundColdInit();
  232.    int n;
  233.    timing = start_time;
  234.  
  235.    n = SoundColdInit (0x388);
  236.    if (!n) {
  237.       printf ("\nSound card not found.\n");
  238.       exit (0);
  239.    }
  240.    mode = MODE;
  241.    SetMode (mode);
  242.  
  243.    SetUp_FNums ();
  244.    for (n=0; n < NR_VOICES; n++) voices [n] = -1;
  245.    for (n=0; n < NR_SOUNDS; n++) {
  246.       myVoice [n] = -1;
  247.       volume [n] = 0x50;
  248.       fnum [n] = 0;
  249.    }
  250. }
  251.  
  252.  
  253. /*-----------------------------------------------------------------------
  254. Given a pitch, return its f-number. */
  255.  
  256. #define  Pitch_to_Fnum(x)  (freq_nums [x % 12])
  257.  
  258. /*
  259. unsigned int Pitch_to_Fnum (pitch)
  260.    int pitch;
  261. {
  262.    return (freq_nums [pitch % 12]);
  263. }
  264. */
  265.  
  266.  
  267. /*-----------------------------------------------------------------------
  268. If the release values have been set to zero, a sound will continue to be
  269. heard even if a note-off is done.  This routine resets the release values
  270. for every voice to an arbitrary value in order to shut them off. */
  271.  
  272. Stifle_Voice (sound_ID)
  273.    int sound_ID;
  274. {
  275.    int voice;
  276.    voice = myVoice [sound_ID];
  277.    if (voice == -1) return;
  278.  
  279.    SetASlotParam (slotVoice [voice][0], prmRelease, 10);
  280.    SetASlotParam (slotVoice [voice][1], prmRelease, 10);
  281.    NoteOff (sound_ID);
  282.    Free_Voice (sound_ID);
  283. }
  284.  
  285. /* Silence all voices. */
  286. Stifle ()
  287. {
  288.    int n;
  289.    for (n=0; n < NR_SOUNDS; n++) Stifle_Voice (n);
  290. }
  291.  
  292.  
  293. /*-----------------------------------------------------------------------
  294. Finds an available voice and sets the approriate entry in the myVoice table.
  295. Returns 0 if no more voices available else returns 1.*/
  296.  
  297. Alloc_Voice (sound_ID)
  298.    int sound_ID;
  299. {
  300.    register int n;
  301.    for (n=0; n < NR_VOICES && voices [n] != -1; n++);
  302.    if (n >= NR_VOICES) return (0);
  303.    voices [n] = sound_ID;
  304.    myVoice [sound_ID] = n;
  305.    return (1);
  306. }
  307.  
  308. /* De-allocate the voice which was allocated in the above routine. */
  309. Free_Voice (sound_ID)
  310.    int sound_ID;
  311. {
  312.    register int n;
  313.    n = myVoice [sound_ID];
  314.    if (n == -1) return;
  315.    voices [n] = -1;
  316.    myVoice [sound_ID] = -1;
  317. }
  318.  
  319.  
  320. /*-----------------------------------------------------------------------
  321. Read the .INS file into the appropriate operator buffers. */
  322.  
  323. Read_Ins_File (name, sound_ID)
  324.    char *name;
  325.    int sound_ID;
  326. {
  327.    unsigned char *op0, *op1;
  328.    int file, n, ok;
  329.    unsigned char temp [20];
  330.  
  331.    op0 = op [sound_ID][0];
  332.    op1 = op [sound_ID][1];
  333.  
  334.    file = open (name, O_RDONLY & O_RAW);
  335.    if (file < 0) {
  336.       printf ("Can't open %s\n", name);
  337.       return (0);
  338.    }
  339.    read (file, temp, 2);
  340.    for (n=0; n < 13; n++) read (file, &op0 [n], 2);
  341.    for (n=0; n < 13; n++) read (file, &op1 [n], 2);
  342.    ok = read (file, temp, 20);
  343.    if (ok) {
  344.       read (file, &op0 [prmWaveSel], 2);
  345.       read (file, &op1 [prmWaveSel], 2);
  346.    }
  347.    close (file);
  348.    return (1);
  349. }
  350.  
  351.  
  352. /*-----------------------------------------------------------------------
  353. Given the timbre parameters, initialize a voice.  NOTE: The real voice
  354. number is passed here, not the sound's ID code. */
  355.  
  356. SetUp_Timbre (voice, op0, op1)
  357.    int voice;
  358.    unsigned char *op0, *op1;
  359. {
  360.    SetCharSlotParam (slotVoice [voice][0], op0, op0 [prmWaveSel]);
  361.    if (mode == MELODIC || voice <= BD)
  362.       SetCharSlotParam (slotVoice [voice][1], op1, op1 [prmWaveSel]);
  363. }
  364.  
  365.  
  366. /*-----------------------------------------------------------------------
  367. Set up an instrument for the passed voice.  Resets the release values
  368. according to the passed flag. This routine is similar to SetVoiceTimbre()
  369. in ADLIB.C.  */
  370.  
  371. Set_Instrument (sound_ID, rel_flag)
  372.    int sound_ID, rel_flag;
  373. {
  374.    int ok, voice, rel_value;
  375.    unsigned char *op0, *op1;
  376.  
  377.    ok = Alloc_Voice (sound_ID);
  378.    if (!ok) {
  379.       printf ("No more voices available\n");
  380.       return (0);
  381.    }
  382.    voice = myVoice [sound_ID];
  383.  
  384.    op0 = op [sound_ID][0];
  385.    op1 = op [sound_ID][1];
  386.  
  387.    /* Setting the release values to zero has the effect of making the note
  388.       play forever. */
  389.    if (rel_flag) rel_value = 1;
  390.       else rel_value = 0;
  391.    op0 [prmRelease] = rel_value;
  392.    op1 [prmRelease] = rel_value;
  393.  
  394.    SetUp_Timbre (voice, op0, op1);
  395.  
  396.    SetVoiceVolume (sound_ID, 0x60);
  397.    return (1);
  398. }
  399.  
  400.  
  401. /*-----------------------------------------------------------------------
  402. Read in all of the required .INS files. */
  403.  
  404. Instrument_Init ()
  405. {
  406.    Read_Ins_File ("instr\\jet11a.ins", JETa);
  407.    Read_Ins_File ("instr\\jet11b.ins", JETb);
  408.    Read_Ins_File ("instr\\oboe1.ins", JETc);
  409.    Read_Ins_File ("instr\\siren2a.ins", JETd);
  410.    Read_Ins_File ("instr\\siren2a.ins", JETe);
  411.  
  412.    Read_Ins_File ("instr\\flute.ins", BOMBa);
  413.    Read_Ins_File ("instr\\plane1.ins", BOMBb);
  414.    Read_Ins_File ("instr\\plane1.ins", BOMBc);
  415.    Read_Ins_File ("instr\\plane1.ins", BOMBd);
  416.  
  417.    Read_Ins_File ("instr\\plane3.ins", PLANEa);
  418.    Read_Ins_File ("instr\\plane3.ins", PLANEb);
  419.  
  420.    Read_Ins_File ("instr\\siren1.ins", SIREN);
  421.    Read_Ins_File ("instr\\helico3.ins", COPTER);
  422. }
  423.  
  424.  
  425. /*------------------------------------------------------------------------
  426. This creates a delay in a rather simple manner in order to create a fake
  427. timer.  In a real application, a scheduling or a timer routine would have
  428. call the Internal_Driver() routine at the appropriate moment. */
  429.  
  430. delay (n)
  431.    int n;
  432. {
  433.    int i, j = 5;
  434.    for (i=0; i < (n * 100); i++) j %= 23;
  435. }
  436.  
  437.  
  438. /*********************************** Jet *********************************/
  439.  
  440. Drive_Jet ()
  441. {
  442.    static int flag = 1;
  443.    static int n = -0x40;
  444.    if (!fnum [JETa]) return;
  445.  
  446.    if (flag) {
  447.       /* raise the volume and pitch */
  448.       SetVoiceVolume (JETa, 0x50+n);
  449.       SetVoiceVolume (JETb, 0x60+n);
  450.       SetVoiceVolume (JETc, 0x30+(n/2));
  451.       SetVoiceVolume (JETd, 0x60+n);
  452.       SetVoiceVolume (JETe, 0x45+n);
  453.       Out_Freq (JETa, fnum [JETa]+n, octave [JETa]);
  454.       Out_Freq (JETb, fnum [JETb]+n, octave [JETb]);
  455.       Out_Freq (JETd, fnum [JETd]+n, octave [JETd]);
  456.       Out_Freq (JETe, fnum [JETe]+n, octave [JETe]);
  457.       n++;
  458.       if (n >= 0x1f) flag = 0;
  459.    }
  460.    else /* flag is 0 */ {
  461.       /* lower the volume and pitch */
  462.       SetVoiceVolume (JETa, 0x50+n);
  463.       SetVoiceVolume (JETb, 0x60+n);
  464.       SetVoiceVolume (JETc, 0x30+(n/2));
  465.       SetVoiceVolume (JETd, 0x60+n);
  466.       SetVoiceVolume (JETe, 0x45+n);
  467.       Out_Freq (JETa, fnum [JETa]+n, octave [JETa]);
  468.       Out_Freq (JETb, fnum [JETb]+n, octave [JETb]);
  469.       Out_Freq (JETd, fnum [JETd]+n, octave [JETd]);
  470.       Out_Freq (JETe, fnum [JETe]+n, octave [JETe]);
  471.       n--;
  472.       if (n <= -0x40) {
  473.          /* "shut off" the jet: set variables to zero */
  474.          flag = 1;
  475.          fnum [JETa] = 0;
  476.          fnum [JETb] = 0;
  477.          fnum [JETc] = 0;
  478.          fnum [JETd] = 0;
  479.          fnum [JETe] = 0;
  480.          Stifle_Voice (JETa);
  481.          Stifle_Voice (JETb);
  482.          Stifle_Voice (JETc);
  483.          Stifle_Voice (JETd);
  484.          Stifle_Voice (JETe);
  485.       }
  486.    }
  487. }
  488.  
  489.  
  490. /*-----------------------------------------------------------------------
  491. This needs five voices to get a good effect.  JET11A.INS and JET11B.INS
  492. alone have been described as being like a vacuum cleaner.  But if ever
  493. you need a vacuum cleaner sound... */
  494.  
  495. Jet ()
  496. {
  497.    int n, ok;
  498.    if (fnum [JETa]) return;
  499.  
  500.    Set_Instrument (JETa, NO_RELEASE);
  501.    Set_Instrument (JETb, NO_RELEASE);
  502.    Set_Instrument (JETc, NO_RELEASE);
  503.    Set_Instrument (JETd, NO_RELEASE);
  504.    ok = Set_Instrument (JETe, NO_RELEASE);
  505.  
  506.    if (!ok) {
  507.       for (n=JETa; n < JETe; n++) Free_Voice (n);
  508.       return;
  509.    }
  510.  
  511.    fnum [JETa] = Pitch_to_Fnum (52);
  512.    fnum [JETb] = Pitch_to_Fnum (88);
  513.    fnum [JETc] = Pitch_to_Fnum (16);
  514.    fnum [JETd] = Pitch_to_Fnum (100);
  515.    fnum [JETe] = Pitch_to_Fnum (101);
  516.    octave [JETa] = 52 / 12;
  517.    octave [JETb] = 88 / 12;
  518.    octave [JETc] = 1;
  519.    octave [JETd] = 100 / 12;
  520.    octave [JETe] = 101 / 12;
  521.  
  522.    /* The pitch of JETc does not vary, so it turned on on here.  The other
  523.       voices are output in Drive_Jet(). */
  524.    SetVoiceVolume (JETc, 0);
  525.    Out_Freq (JETc, fnum [JETc], octave [JETc]);
  526.    Drive_Jet ();
  527.  
  528.    printf ("Jet started\n");
  529. }
  530.  
  531.  
  532. /*********************************** Bomb ********************************/
  533.  
  534. unsigned bomb_low;
  535. int wait_time;
  536.  
  537. Drive_Bomb ()
  538. {
  539.    if (!fnum [BOMBa]) return;
  540.  
  541.    if (fnum [BOMBa] > bomb_low) {
  542.       /* Decrease pitch of bomb falling sound */
  543.       fnum [BOMBa] -= 4;
  544.       Out_Freq (BOMBa, fnum [BOMBa], octave [BOMBa]);
  545.    }
  546.    else {
  547.       if (!wait_time) {
  548.          /* Shut off the flute (bomb falling sound) */
  549.          Stifle_Voice (BOMBa);
  550.  
  551.          /* Turn on the exploding sound. The more voices which can be used
  552.             here, the better the effect, although the minimum is two voices.
  553.             For each voice added, set the pitch a half-tone above the
  554.             previous voice. */
  555.          Out_Freq (BOMBb, fnum [BOMBb], octave [BOMBb]);
  556.          Out_Freq (BOMBc, fnum [BOMBc], octave [BOMBc]);
  557.          Out_Freq (BOMBd, fnum [BOMBd], octave [BOMBd]);
  558.          wait_time = 160;
  559.       }
  560.       else {  /* wait to let sound fade, otherwise it may get cut off */
  561.          wait_time--;
  562.          if (!wait_time) {  /* wait has expired, free voices */
  563.             timing += 5;
  564.             fnum [BOMBa] = 0;
  565.             Stifle_Voice (BOMBb);
  566.             Stifle_Voice (BOMBc);
  567.             Stifle_Voice (BOMBd);
  568.          }
  569.       }
  570.    }
  571. }
  572.  
  573. /*-----------------------------------------------------------------------*/
  574.  
  575. Bomb ()
  576. {
  577.    int new_oct, ok;
  578.    unsigned char c;
  579.  
  580.    if (!fnum [BOMBa]) {
  581.       Set_Instrument (BOMBb, RELEASE);
  582.       Set_Instrument (BOMBc, RELEASE);
  583.       Set_Instrument (BOMBd, RELEASE);
  584.       ok = Set_Instrument (BOMBa, NO_RELEASE);
  585.       if (!ok) {
  586.          Free_Voice (BOMBb);
  587.          Free_Voice (BOMBc);
  588.          Free_Voice (BOMBd);
  589.          return;
  590.       }
  591.  
  592.       timing -= 5;
  593.       fnum [BOMBb] = Pitch_to_Fnum (12);
  594.       fnum [BOMBc] = Pitch_to_Fnum (13);
  595.       fnum [BOMBd] = Pitch_to_Fnum (14);
  596.       octave [BOMBb] = 1;
  597.       octave [BOMBc] = 1;
  598.       octave [BOMBd] = 1;
  599.       SetVoiceVolume (BOMBb, 0x7f);
  600.       SetVoiceVolume (BOMBc, 0x7f);
  601.       SetVoiceVolume (BOMBd, 0x7f);
  602.       SetVoiceVolume (BOMBa, volume [BOMBa]);
  603.    }
  604.  
  605.    octave [BOMBa] = 95 / 12;
  606.    fnum [BOMBa] = Pitch_to_Fnum (95);
  607.    bomb_low = fnum [BOMBa] >> 2;
  608.    wait_time = 0;
  609.  
  610.    Out_Freq (BOMBa, fnum [BOMBa], octave [BOMBa]);
  611.  
  612.    /* Read input from keyboard */
  613.    printf ("\n Bomb: press spacebar to exit, 'S' to silence ");
  614.    do {
  615.       while (! kbhit ())
  616.          Internal_Driver ();
  617.       c = getch ();
  618.       if (c) {
  619.          c = toupper (c);
  620.          if (c == 'S') {
  621.             timing += 5;
  622.             Stifle_Voice (BOMBa);
  623.             Stifle_Voice (BOMBb);
  624.             Stifle_Voice (BOMBc);
  625.             Stifle_Voice (BOMBd);
  626.             fnum [BOMBa] = 0;
  627.             c = ' ';
  628.          }
  629.       }
  630.       else {
  631.          c = getch ();
  632.       }
  633.    }
  634.    while (c != ' ');
  635. }
  636.  
  637.  
  638. /********************************* Airplane ******************************/
  639.  
  640. Drive_Airplane ()
  641. {
  642.    /* Here, one could adjust the pitch up and down slightly in order to
  643.       give the sound a more pronounced whine. */
  644. }
  645.  
  646.  
  647. /*-----------------------------------------------------------------------*/
  648.  
  649. Airplane ()
  650. {
  651.    static int inc = 6;
  652.    static pitch = 30;
  653.    int n, new_oct, ok;
  654.    unsigned char c;
  655.  
  656.    if (!fnum [PLANEb]) {
  657.       ok = Set_Instrument (PLANEa,  NO_RELEASE);
  658.       if (!ok) return;
  659.       ok = Set_Instrument (PLANEb,  NO_RELEASE);
  660.       if (!ok) {
  661.          Free_Voice (PLANEa);
  662.          return;
  663.       }
  664.  
  665.       timing -= 5;
  666.       volume [PLANEb] = volume [PLANEa] * 3 / 4;
  667.       octave [PLANEa] = octave [PLANEb] = pitch / 12;
  668.       fnum [PLANEa] = Pitch_to_Fnum (pitch);
  669.       fnum [PLANEb] = fnum [PLANEa] + 2;
  670.  
  671.       SetVoiceVolume (PLANEa, volume [PLANEa]);
  672.       SetVoiceVolume (PLANEb, volume [PLANEb]);
  673.       Out_Freq (PLANEa, fnum [PLANEa], octave [PLANEa]);
  674.    }
  675.  
  676.    /* Read keyboard input */
  677.    printf ("\nAirplane: press spacebar to exit, 'S' to silence ");
  678.    do {
  679.       while (! kbhit ())
  680.          Internal_Driver ();
  681.       c = getch ();
  682.       if (c) {
  683.          c = toupper (c);
  684.          if (c == 'S') {
  685.             timing += 5;
  686.             Stifle_Voice (PLANEa);
  687.             Stifle_Voice (PLANEb);
  688.             fnum [PLANEa] = 0;
  689.             fnum [PLANEb] = 0;
  690.             c = ' ';
  691.          }
  692.       }
  693.       else {
  694.          c = getch ();
  695.          switch (c) {
  696.             case LEFT:
  697.                 /* Decrease volume */
  698.                 if (volume [PLANEa] == 0) break;
  699.                 volume [PLANEa] -= 2;
  700.                 volume [PLANEb] = volume [PLANEa] * 3 / 4;
  701.                 SetVoiceVolume (PLANEa, volume [PLANEa]);
  702.                 SetVoiceVolume (PLANEb, volume [PLANEb]);
  703.                 break;
  704.             case RIGHT:
  705.                 /* Increase volume */
  706.                 if (volume [PLANEa] >= 0x7e) break;
  707.                 volume [PLANEa] += 2;
  708.                 volume [PLANEb] = volume [PLANEa] * 3 / 4;
  709.                 SetVoiceVolume (PLANEa, volume [PLANEa]);
  710.                 SetVoiceVolume (PLANEb, volume [PLANEb]);
  711.                 break;
  712.             case UP:
  713.                 /* Increase pitch */
  714.                 fnum [PLANEa] += 2;
  715.                 Out_Freq (PLANEa, fnum [PLANEa], octave [PLANEa]);
  716.                 fnum [PLANEb] = fnum [PLANEa] + 1;
  717.                 break;
  718.             case DOWN:
  719.                 /* Decrease pitch */
  720.                 if (fnum [PLANEa] <= 18) break;
  721.                 fnum [PLANEa] -= 2;
  722.                 Out_Freq (PLANEa, fnum [PLANEa], octave [PLANEa]);
  723.                 fnum [PLANEb] = fnum [PLANEa] + 1;
  724.                 break;
  725.          }
  726.       }
  727.    }
  728.    while (c != ' ');
  729. }
  730.  
  731.  
  732. /*********************************** Siren *******************************/
  733.  
  734. unsigned siren_high;
  735. unsigned siren_low;
  736.  
  737. Drive_Siren () {
  738.    static flag = 1;
  739.    if (!fnum [SIREN]) return;
  740.  
  741.    if (flag) {
  742.       /* increase pitch */
  743.       fnum [SIREN] += 4;
  744.       Out_Freq (SIREN, fnum [SIREN], octave [SIREN]);
  745.       if (fnum [SIREN] >= siren_high) flag = 0;
  746.    }
  747.    else {
  748.       /* decrease pitch */
  749.       fnum [SIREN] -= 4;
  750.       Out_Freq (SIREN, fnum [SIREN], octave [SIREN]);
  751.       if (fnum [SIREN] <= siren_low) flag = 1;
  752.    }
  753. }
  754.  
  755.  
  756. /*-----------------------------------------------------------------------*/
  757.  
  758. Siren ()
  759. {
  760.    int ok;
  761.    unsigned char c;
  762.  
  763.    if (!fnum [SIREN]) {
  764.       ok = Set_Instrument (SIREN, NO_RELEASE);
  765.       if (!ok) return;
  766.       timing -= 5;
  767.       volume [SIREN] = 0x60;
  768.       octave [SIREN] = 67 / 12;
  769.       fnum [SIREN] = Pitch_to_Fnum (67) >> 1;
  770.       siren_low = Pitch_to_Fnum (66);
  771.       siren_high = Pitch_to_Fnum (71);
  772.  
  773.       SetVoiceVolume (SIREN, volume [SIREN]);
  774.       Out_Freq (SIREN, fnum [SIREN], octave [SIREN]);
  775.    }
  776.  
  777.    /* Read keyboard input */
  778.    printf ("\nSiren: press spacebar to exit, 'S' to silence ");
  779.    do {
  780.       while (! kbhit ())
  781.          Internal_Driver ();
  782.       c = getch ();
  783.       if (c) {
  784.          c = toupper (c);
  785.          if (c == 'S') {
  786.             timing += 5;
  787.             Stifle_Voice (SIREN);
  788.             fnum [SIREN] = 0;
  789.             c = ' ';
  790.          }
  791.       }
  792.       else {
  793.          c = getch ();
  794.          switch (c) {
  795.             case LEFT:
  796.                 /* decrease volume */
  797.                 if (volume [SIREN] == 0) break;
  798.                 volume [SIREN] -= 2;
  799.                 SetVoiceVolume (SIREN, volume [SIREN]);
  800.                 break;
  801.             case RIGHT:
  802.                 /* increase volume */
  803.                 if (volume [SIREN] == 0x7f) break;
  804.                 volume [SIREN] += 2;
  805.                 SetVoiceVolume (SIREN, volume [SIREN]);
  806.                 break;
  807.             case UP:
  808.                 /* increase pitch */
  809.                 fnum [SIREN] += 8;
  810.                 Out_Freq (SIREN, fnum [SIREN], octave [SIREN]);
  811.                 break;
  812.             case DOWN:
  813.                 /* decrease pitch */
  814.                 fnum [SIREN] -= 8;
  815.                 Out_Freq (SIREN, fnum [SIREN], octave [SIREN]);
  816.                 break;
  817.          }
  818.       }
  819.    }
  820.    while (c != ' ');
  821. }
  822.  
  823.  
  824. /******************************** Helicopter *****************************/
  825.  
  826. int heli_wait;
  827.  
  828. Drive_Helicopter ()
  829. {
  830.    static int cntr = 0;
  831.    if (!fnum [COPTER]) return;
  832.  
  833.    cntr++;
  834.    if (cntr >= heli_wait) {
  835.       NoteOff (COPTER);
  836.       Out_Freq (COPTER, fnum [COPTER], octave [COPTER]);
  837.       cntr = 0;
  838.    }
  839. }
  840.  
  841.  
  842. /*-----------------------------------------------------------------------*/
  843.  
  844. Helicopter ()
  845. {
  846.    int pitch, ok;
  847.    unsigned char c;
  848.  
  849.    if (!fnum [COPTER]) {
  850.       ok = Set_Instrument (COPTER, RELEASE);
  851.       if (!ok) return;
  852.       timing -= 5;
  853.       heli_wait = 5;
  854.       pitch = 39;
  855.       volume [COPTER] = 0x70;
  856.       octave [COPTER] = pitch / 12;
  857.       fnum [COPTER] = Pitch_to_Fnum (pitch);
  858.  
  859.       SetVoiceVolume (COPTER, volume [COPTER]);
  860.       Out_Freq (COPTER, fnum [COPTER], octave [COPTER]);
  861.    }
  862.  
  863.    /* Read keyboard input */
  864.    printf ("\nHelicopter: Pg Up - faster, Pg Dn - slower, spacebar to exit ");
  865.    do {
  866.       while (! kbhit ()) Internal_Driver ();
  867.       c = getch ();
  868.       if (c) {
  869.          c = toupper (c);
  870.          if (c == 'S') {
  871.             timing += 5;
  872.             Stifle_Voice (COPTER);
  873.             fnum [COPTER] = 0;
  874.             c = ' ';
  875.          }
  876.       }
  877.       else {
  878.          c = getch ();
  879.          switch (c) {
  880.             case LEFT:
  881.                 /* decrease volume */
  882.                 if (volume [COPTER] == 0) break;
  883.                 volume [COPTER] -= 2;
  884.                 SetVoiceVolume (COPTER, volume [COPTER]);
  885.                 break;
  886.             case RIGHT:
  887.                 /* increase volume */
  888.                 if (volume [COPTER] == 0x7f) break;
  889.                 volume [COPTER] += 2;
  890.                 SetVoiceVolume (COPTER, volume [COPTER]);
  891.                 break;
  892.             case UP:
  893.                 /* increase pitch */
  894.                 fnum [COPTER] += 2;
  895.                 break;
  896.             case DOWN:
  897.                 /* decrease pitch */
  898.                 if (fnum [COPTER] > 0) fnum [COPTER] -= 2;
  899.                 break;
  900.             case PAGEUP:
  901.                 /* increase frequency of note off/on */
  902.                 if (heli_wait > 0) heli_wait--;
  903.                 break;
  904.             case PAGEDOWN:
  905.                 /* decrease frequency of note off/on */
  906.                 if (heli_wait < 0x7fff) heli_wait++;
  907.                 break;
  908.          }
  909.       }
  910.    }
  911.    while (c != ' ');
  912. }
  913.  
  914.  
  915. /************************************************************************
  916. Routine which allows one to read in an instrument file and play with
  917. the pitch and volume.  */
  918.  
  919. Test ()
  920. {
  921. #ifdef TEST
  922.    char fname [40];
  923.    int pitch, octave, volume;
  924.    unsigned int fnum1;
  925.    unsigned char c;
  926.  
  927.    printf ("\nFile name: ");
  928.    scanf ("%s", fname);
  929.    Read_Ins_File (fname, FAKE_ID);
  930.    if (!Set_Instrument (FAKE_ID, NO_RELEASE)) return;
  931.    printf ("Start pitch: ");
  932.    scanf ("%d", &pitch);
  933.  
  934.    volume = 0x60;
  935.    octave = pitch / 12;
  936.    fnum1 = Pitch_to_Fnum (pitch);
  937.    SetVoiceVolume (FAKE_ID, volume);
  938.    Out_Freq (FAKE_ID, fnum1, octave);
  939.  
  940.    printf ("\nTest: press spacebar to stop ");
  941.    do {
  942.       while (! kbhit ());
  943.       c = getch ();
  944.       if (!c) {
  945.          c = getch ();
  946.          switch (c) {
  947.             case LEFT:
  948.                 /* decrease volume */
  949.                 if (volume == 0) break;
  950.                 volume -= 2;
  951.                 SetVoiceVolume (FAKE_ID, volume);
  952.                 break;
  953.             case RIGHT:
  954.                 /* increase volume */
  955.                 if (volume == 0x7f) break;
  956.                 volume += 2;
  957.                 SetVoiceVolume (FAKE_ID, volume);
  958.                 break;
  959.             case UP:
  960.                 /* increase pitch */
  961.                 fnum1 += 5;
  962.                 Out_Freq (FAKE_ID, fnum1, octave);
  963.                 break;
  964.             case DOWN:
  965.                 /* decrease pitch */
  966.                 fnum1 -= 4;
  967.                 Out_Freq (FAKE_ID, fnum1, octave);
  968.                 break;
  969.          }
  970.       }
  971.    }
  972.    while (c != ' ');
  973.    printf (" fnum=%d, octave=%d, vol=%d ", fnum1, octave, volume);
  974.    Stifle ();
  975. #endif
  976. }
  977.  
  978.  
  979. /************************************************************************
  980. This routine must be called constantly (or at a constant interval) in order
  981. to keep producing the sound effects.  In a real application, this routine or
  982. a variation of it would be hooked up to the timer interrupt directly or via
  983. another routine. */
  984.  
  985. Internal_Driver ()
  986. {
  987.    Drive_Siren ();
  988.    Drive_Airplane ();
  989.    Drive_Bomb ();
  990.    Drive_Helicopter ();
  991.    Drive_Jet ();
  992.    delay (timing);
  993. }
  994.  
  995.  
  996. /*-----------------------------------------------------------------------
  997. Main loop.  Displays menu and reads user's selection. */
  998.  
  999. Choose_Sound ()
  1000. {
  1001.    unsigned char c;
  1002.    printf (
  1003.      "\nTo speed up or slow down, re-execute using /Txxx, where xxx is a"
  1004.      "\nnumber.   xxx is currently %d.", timing);
  1005.    printf ("\n\nUse up and down arrows to change pitch.  Use left and\n"
  1006.            "right arrows to change volume.");
  1007.    do {
  1008.       printf ("\nChoose one:\n   A)irplane\n   B)omb\n   "
  1009.               "H)elicopter\n   J)et\n   S)iren\n   R)eset all\n   Q)uit\n");
  1010.       while (! kbhit ()) Internal_Driver ();
  1011.       c = getch ();
  1012.       if (!c) getch ();
  1013.       else {
  1014.          c = toupper (c);
  1015.          switch (c) {
  1016.             case 'A': Airplane ();
  1017.                       break;
  1018.             case 'B': Bomb ();
  1019.                       break;
  1020.             case 'H': Helicopter ();
  1021.                       break;
  1022.             case 'J': Jet ();
  1023.                       break;
  1024.             case 'R': Stifle ();
  1025.                       Demo_Init ();
  1026.                       break;
  1027.             case 'S': Siren ();
  1028.                       break;
  1029.             case 'T': Test ();
  1030.                       break;
  1031.          }
  1032.       }
  1033.    }
  1034.    while (c != 'Q');
  1035. }
  1036.  
  1037.  
  1038.